package com.bike.bikeserver.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.bike.bikeserver.common.utils.ApplicationHelperUtils;
import com.bike.bikeserver.domain.FeedbackSaying;
import com.bike.bikeserver.domain.vo.FeedbackMsgVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static com.bike.bikeserver.constant.Constants.SERVER_NICKNAME;
import static com.bike.bikeserver.constant.Constants.SERVER_URL;


/**
 * @version 1.0
 * @Author ZZX
 * @Date 2022/12/12 21:25
 */
@Component
@Slf4j
@ServerEndpoint("/websocket/{nickName}") //暴露的ws应用的路径
public class WebSocketUtils
{
    private FeedbackSayingServiceImpl feedbackSayingService = (FeedbackSayingServiceImpl) ApplicationHelperUtils.getBean("feedbackSayingService");

    private Session session;
    private String nickName;

    private static CopyOnWriteArrayList<WebSocketUtils> webSocketSet = new CopyOnWriteArrayList<>();

    private static HashSet<String> nickNameSet = new HashSet<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("nickName") String nickName)
    {
        // 过滤未登录用户
        if (nickName == null)
        {
            return;
        }
        // 查找该用户是否已连接
        if (nickNameSet.contains(nickName))
        {
            return;
        }
        nickNameSet.add(nickName);
        this.session = session;
        this.nickName = nickName;
        webSocketSet.add(this);
        log.info("[websocket消息提示] 有新的连接，用户[" + nickName + "]加入连接，连接总数为{}", webSocketSet.size());
        // 从数据库中查找出用户的被人工客服回复的消息（只要不是待回复，就发给用户）
        List<FeedbackSaying> unFeedBackSayingByNickName = feedbackSayingService.getUnFeedBackSayingByNickName(nickName);
        for (FeedbackSaying saying : unFeedBackSayingByNickName)
        {
            FeedbackMsgVo feedbackMsgVo = new FeedbackMsgVo(SERVER_NICKNAME, SERVER_URL, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), saying.getFanswer());
            this.sendMessageByNickName(JSON.toJSONString(feedbackMsgVo), nickName);
        }
    }

    @OnClose
    public void onClose()
    {
        webSocketSet.remove(this);
        nickNameSet.remove(this.nickName);
        log.info("[websocket消息提示] 有一个连接断开，总数为{}", webSocketSet.size());
    }

    /**
     * 处理来自客户端的消息
     *
     * @param message
     */
    @OnMessage
    public void onMessage(String message)
    {
        // 过滤未登录用户
        if (nickName == null)
        {
            return;
        }
        log.info("[websocket消息提示] 收到消息：{}", message);
        // 这里对接受的数据进行处理
        if ("ping".equals(message))
        {
            this.sendMessage("I am alive...");
        }
        else
        {
            // 从message中解析出nickName等数据
            JSONObject msg = JSONObject.parseObject(message);
            // 并积累一定的操作到表中（并记录时间），然后用户只要建立链接，就将存储的信息反馈给用户
            FeedbackMsgVo feedbackMsgVo = new FeedbackMsgVo(SERVER_NICKNAME, SERVER_URL, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), "收到！已将您的问题提交至人工客服，请耐心等待，一般为一个工作日");
            // 组装反馈对象，入库，由后台管理系统处理
            FeedbackSaying feedbackSaying = new FeedbackSaying();
            feedbackSaying.setFquestion(msg.getString("message"));
            feedbackSaying.setAsker(msg.getString("nickName"));
            feedbackSaying.setFanswer("待回复");
            try
            {
                feedbackSayingService.saveFeedbackSaying(feedbackSaying);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            this.sendMessageByNickName(JSON.toJSONString(feedbackMsgVo), msg.getString("nickName"));
        }
    }

    /**
     * 向客户端发送信息（群发广播）
     *
     * @param message
     */
    public void sendMessage(String message)
    {
        log.info("[websocket消息提示] 广播消息，message：{}", message);
        for (WebSocketUtils webSocket : webSocketSet)
        {
            try
            {
                webSocket.session.getBasicRemote().sendText(message);
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

    /**
     * 给订阅了特定主题的客户端发送消息
     *
     * @param message
     * @param nickName
     */
    public void sendMessageByNickName(String message, String nickName)
    {
        log.info("[websocket消息提示] 给用户[" + nickName + "]发送消息，message：{}", message);
        for (WebSocketUtils webSocket : webSocketSet)
        {
            try
            {
                if (nickName.equals(webSocket.nickName))
                {
                    webSocket.session.getBasicRemote().sendText(message);
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

}
